package br.com.cfccultura.admin.model.dao.mysql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import br.com.cfccultura.admin.model.dao.IAlunoDAO;
import br.com.cfccultura.admin.model.dao.IAvaliacaoCursoDAO;
import br.com.cfccultura.admin.model.dao.ICalendarioDAO;
import br.com.cfccultura.admin.model.dao.ICursoDAO;
import br.com.cfccultura.admin.model.dao.ITurmaDAO;
import br.com.cfccultura.admin.model.enums.Periodo;
import br.com.cfccultura.admin.model.vo.Aluno;
import br.com.cfccultura.admin.model.vo.AvaliacaoCurso;
import br.com.cfccultura.admin.model.vo.CalendarioDia;
import br.com.cfccultura.admin.model.vo.Curso;
import br.com.cfccultura.admin.model.vo.Funcionario;
import br.com.cfccultura.admin.model.vo.Matricula;
import br.com.cfccultura.admin.model.vo.OrdemServico;
import br.com.cfccultura.admin.model.vo.Sala;
import br.com.cfccultura.admin.model.vo.Turma;

@Repository
public class TurmaDAO extends DAO implements ITurmaDAO {
	
	@Autowired
	private ICalendarioDAO calendarioDAO;
	
	@Autowired
	private ICursoDAO cursoDAO;
	
	@Autowired
	private IAlunoDAO alunoDAO;
	
	@Autowired
	private IAvaliacaoCursoDAO avaliacaoCursoDAO;
	
	@Value("${taxi.codigo.turma.inicio}")
	private Integer codigoInicioTurmaTaxi;
	@Value("${taxi.codigo.aluno.inicio}")
	private Integer codigoInicioAlunoTaxi;

	@Override
	public Turma consultar(Integer codigo) {
		List<Turma> list = consultar(new Turma(codigo));
		if(list.size() > 0) {
			return list.get(0);
		}
		throw new RuntimeException("Turma não encontrada! - " + codigo);
	}
	
	@Override
	public List<Turma> consultar(Turma vo) {

		String sql = 
				"SELECT turma.cd_turma, turma.id_periodo, turma.cd_curso, turma.cd_sala_aula, sala.dc_sala_aula, sala.qt_aluno, turma.cd_funcionario_instrutor, funcionario.nm_funcionario, turma.dt_inicio, turma.dt_termino, turma.cd_turma_taxi " +
				"FROM tb_turma turma, tb_sala_aula sala, tb_funcionario funcionario where ";
		List<Object> params = new ArrayList<Object>();
		if(vo.getCodigo() != null) {
			sql += "turma.cd_turma = ? and ";
			params.add(vo.getCodigo());
		} else if(vo.getInicio() != null && vo.getTermino() != null) {
			sql += "turma.dt_inicio <= DATE(?) and turma.dt_termino >= DATE(?) and ";
			params.add(vo.getInicio());
			params.add(vo.getTermino());
		} else if(vo.getInicio() != null && vo.getTermino() == null) {
			sql += "turma.dt_inicio >= DATE(?) and ";
			params.add(vo.getInicio());
		}
		
		if(vo.getPeriodo() != null) {
			sql += "turma.id_periodo = ? and ";
			params.add(vo.getPeriodo().getValue());
		}
		
		if(vo.getCurso() != null && vo.getCurso().getCodigo() != null) {
			sql += "turma.cd_curso = ? and ";
			params.add(vo.getCurso().getCodigo());
		}
		sql += "sala.cd_sala_aula = turma.cd_sala_aula and " +
				"turma.cd_funcionario_instrutor = funcionario.cd_funcionario " +
				"order by dt_inicio";

		List<Turma> result = query(sql, params.toArray(), new RowMapper<Turma>() {
			@Override
			public Turma mapRow(ResultSet rs, int arg1) throws SQLException {
				Turma vo = new Turma();
				vo.setCodigo(rs.getInt("turma.cd_turma"));
				vo.setPeriodo(Periodo.setValue(rs.getInt("turma.id_periodo")));
				Curso curso = getCursoDAO().consultar(rs.getInt("turma.cd_curso"));
				vo.setCurso(curso);
				vo.setSala(new Sala(rs.getInt("turma.cd_sala_aula")));
				vo.getSala().setDescricao(rs.getString("sala.dc_sala_aula"));
				vo.getSala().setQtdeAluno(rs.getInt("sala.qt_aluno"));
				vo.setInstrutor(new Funcionario(rs.getInt("turma.cd_funcionario_instrutor")));
				vo.getInstrutor().setNome(rs.getString("funcionario.nm_funcionario"));
				vo.setInicio(rs.getDate("turma.dt_inicio"));
				vo.setTermino(rs.getDate("turma.dt_termino"));
				if(rs.getObject("turma.cd_turma_taxi") != null)
					vo.setCodigoTaxi(rs.getInt("turma.cd_turma_taxi"));
				carregaMatriculas(vo);
				carregarDiaCalendario(vo);
				return vo;
			}			
		});
		
		return result;
	}
	
	private void carregaMatriculas(Turma vo) {
		String sql = "SELECT matricula.cd_turma_matricula, matricula.cd_aluno, matricula.dt_cadastro, aluno.dc_nome, aluno.dc_cpf, aluno.dc_rg, item.cd_ordem_servico, matricula.cd_avaliacao_curso, matricula.cd_matricula_taxi " +
				"FROM tb_turma_matricula matricula, tb_aluno aluno, tb_ordem_servico_item item " +
				"WHERE matricula.cd_aluno = aluno.cd_aluno and item.cd_turma_matricula = matricula.cd_turma_matricula and matricula.cd_turma = ? " +
				"ORDER BY aluno.dc_nome";
		List<Matricula> result = query(sql, new Object[] {vo.getCodigo()}, new RowMapper<Matricula>() {
			@Override
			public Matricula mapRow(ResultSet rs, int arg1) throws SQLException {
				Matricula vo = new Matricula();
				vo.setCodigo(rs.getInt("matricula.cd_turma_matricula"));
				vo.setAluno(new Aluno(rs.getInt("matricula.cd_aluno")));
				vo.getAluno().setNome(rs.getString("aluno.dc_nome"));
				vo.getAluno().setCpf(rs.getString("aluno.dc_cpf"));
				vo.getAluno().setRg(rs.getString("aluno.dc_rg"));
				vo.setCadastro(rs.getDate("matricula.dt_cadastro"));
				vo.setOrdemServico(new OrdemServico(rs.getInt("item.cd_ordem_servico")));
				if(rs.getObject("matricula.cd_avaliacao_curso") != null) {
					vo.setAvaliacao(getAvaliacaoCursoDAO().consultar(rs.getInt("matricula.cd_avaliacao_curso")));
				}
				if(rs.getObject("matricula.cd_matricula_taxi") != null)
					vo.setCodigoTaxi(rs.getInt("matricula.cd_matricula_taxi"));
				
				return vo;
			}			
		});
		vo.setMatriculas(result);
	}
	
	private void carregarDiaCalendario(Turma vo) {
		String sql = "SELECT calendario.dt_calendario FROM tb_calendario_turma turma, tb_calendario calendario WHERE turma.cd_calendario = calendario.cd_calendario and turma.cd_turma = ?";
		List<CalendarioDia> result = query(sql, new Object[] {vo.getCodigo()}, new RowMapper<CalendarioDia>() {
			@Override
			public CalendarioDia mapRow(ResultSet rs, int arg1) throws SQLException {
				CalendarioDia vo = new CalendarioDia();
				vo.setData(rs.getDate("calendario.dt_calendario"));
				return vo;
			}
		});
		vo.setCalendario(result);
	}
	
	@Override
	public void incluir(final Turma vo) {
		
		KeyHolder keyHolder = new GeneratedKeyHolder();
		update(new PreparedStatementCreator() {			
			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				String sql = "INSERT INTO tb_turma(id_periodo, cd_curso, cd_sala_aula, cd_funcionario_instrutor, dt_inicio, dt_termino) values(?,?,?,?,?,?)";
				PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

			    ps.setInt(1, vo.getPeriodo().getValue());
				ps.setInt(2, vo.getCurso().getCodigo());
				ps.setInt(3, vo.getSala().getCodigo());
				ps.setInt(4, vo.getInstrutor().getCodigo());
				ps.setDate(5, new java.sql.Date(vo.getInicio().getTime()));
				ps.setDate(6, new java.sql.Date(vo.getTermino().getTime()));				 
			    return ps;
			}
		}, keyHolder);
		
		vo.setCodigo(keyHolder.getKey().intValue());
		
		String sql = "INSERT INTO tb_calendario_turma values(?,?)";
		for(CalendarioDia dia : vo.getCalendario()) {
			dia = getCalendarioDAO().consultar(dia.getData());
			update(sql, new Object[] {dia.getCodigo(), vo.getCodigo()});
		}
		
	}
	
	@Override
	public void alterar(Turma vo) {
		String sql = "UPDATE tb_turma set id_periodo = ?, cd_curso = ?, cd_sala_aula = ?, cd_funcionario_instrutor = ?, dt_inicio = ?, dt_termino = ? where cd_turma = ?";
		update(sql, new Object[] {vo.getPeriodo().getValue(), vo.getCurso().getCodigo(), vo.getSala().getCodigo(), vo.getInstrutor().getCodigo(), vo.getInicio(), vo.getTermino(), vo.getCodigo()});
		
		sql = "DELETE FROM tb_calendario_turma where cd_turma = ?";
		update(sql, new Object[]{vo.getCodigo()});
		
		sql = "INSERT INTO tb_calendario_turma values(?,?)";
		for(CalendarioDia dia : vo.getCalendario()) {
			dia = getCalendarioDAO().consultar(dia.getData());
			update(sql, new Object[] {dia.getCodigo(), vo.getCodigo()});
		}		
	}
	
	@Override
	public void excluir(Integer id) {
		try {
			String sql = "DELETE FROM tb_turma where cd_turma = ?";
			update(sql, new Object[] {id});
		} catch(DataIntegrityViolationException e) {
			throw new RuntimeException("Error deleting Turma -> " + id);
		}
	}
	
	@Override
	public void matricular(final Matricula vo) {
		KeyHolder keyHolder = new GeneratedKeyHolder();
		update(new PreparedStatementCreator() {			
			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				String sql = "INSERT INTO tb_turma_matricula(cd_turma, cd_aluno, dt_cadastro, cd_funcionario_cadastro) values(?,?,?,?)";
				PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

			    ps.setInt(1, vo.getTurma().getCodigo());
				ps.setInt(2, vo.getAluno().getCodigo());
				ps.setTimestamp(3, new java.sql.Timestamp(System.currentTimeMillis()));
				ps.setInt(4, vo.getUsuarioSistema().getCodigo());	 
			    return ps;
			}
		}, keyHolder);
		
		vo.setCodigo(keyHolder.getKey().intValue());
	}	
	
	@Override
	public Matricula consultarMatricula(Matricula vo) {
		String sql = "SELECT matricula.cd_turma_matricula, matricula.cd_aluno, matricula.dt_cadastro, item.cd_ordem_servico, matricula.cd_turma " +
				"FROM tb_turma_matricula matricula, tb_aluno aluno, tb_ordem_servico_item item " +
				"WHERE matricula.cd_aluno = aluno.cd_aluno and matricula.cd_turma_matricula = item.cd_turma_matricula and matricula.cd_turma_matricula = ?";
		Matricula matricula = queryForObject(sql, new Object[] {vo.getCodigo()}, new RowMapper<Matricula>() {
			@Override
			public Matricula mapRow(ResultSet rs, int arg1) throws SQLException {
				Matricula vo = new Matricula();
				vo.setCodigo(rs.getInt("matricula.cd_turma_matricula"));
				vo.setAluno(getAlunoDAO().consultar(rs.getInt("matricula.cd_aluno")));
				vo.setCadastro(rs.getDate("matricula.dt_cadastro"));
				vo.setTurma(new Turma(rs.getInt("matricula.cd_turma")));
				vo.setOrdemServico(new OrdemServico(rs.getInt("item.cd_ordem_servico")));
				return vo;
			}			
		});
		
		matricula.setTurma(consultar(matricula.getTurma().getCodigo()));

		return matricula;
	}
	
	@Override
	public void gerarTurmaTaxi(Turma vo) {
		String sql = "SELECT max(cd_turma_taxi) from tb_turma";
		Integer turmaTaxi = queryForInt(sql);
		if(turmaTaxi == 0)
			turmaTaxi = getCodigoInicioTurmaTaxi();
		
		sql = "UPDATE tb_turma set cd_turma_taxi = ? where cd_turma = ?";
		update(sql, new Object[] {turmaTaxi, vo.getCodigo()});
		
		sql = "SELECT max(cd_matricula_taxi) from tb_turma_matricula";
		Integer matriculaTaxi = queryForInt(sql);
		if(matriculaTaxi == 0)
			matriculaTaxi = getCodigoInicioAlunoTaxi();
		
		for(Matricula matricula : vo.getMatriculas()) {
			if(matricula.getAvaliacao() == null || !matricula.getAvaliacao().getAprovado())
				continue;
			
			sql = "UPDATE tb_turma_matricula set cd_matricula_taxi = ? where cd_turma_matricula = ?";
			update(sql, new Object[] {matriculaTaxi++, matricula.getCodigo()});			
		}
	}
	
	@Override
	public void atualizarAvaliacao(Integer codigoMatricula, AvaliacaoCurso avaliacao) {
		String sql = "UPDATE tb_turma_matricula set cd_avaliacao_curso = ? where cd_turma_matricula = ?";
		update(sql, new Object[] {avaliacao.getCodigo(), codigoMatricula});		
	}
	
	public ICalendarioDAO getCalendarioDAO() {
		return calendarioDAO;
	}
	public void setCalendarioDAO(ICalendarioDAO calendarioDAO) {
		this.calendarioDAO = calendarioDAO;
	}
	public ICursoDAO getCursoDAO() {
		return cursoDAO;
	}
	public void setCursoDAO(ICursoDAO cursoDAO) {
		this.cursoDAO = cursoDAO;
	}
	public IAlunoDAO getAlunoDAO() {
		return alunoDAO;
	}
	public void setAlunoDAO(IAlunoDAO alunoDAO) {
		this.alunoDAO = alunoDAO;
	}
	public IAvaliacaoCursoDAO getAvaliacaoCursoDAO() {
		return avaliacaoCursoDAO;
	}
	public void setAvaliacaoCursoDAO(IAvaliacaoCursoDAO avaliacaoCursoDAO) {
		this.avaliacaoCursoDAO = avaliacaoCursoDAO;
	}
	public Integer getCodigoInicioAlunoTaxi() {
		return codigoInicioAlunoTaxi;
	}
	public void setCodigoInicioAlunoTaxi(Integer codigoInicioAlunoTaxi) {
		this.codigoInicioAlunoTaxi = codigoInicioAlunoTaxi;
	}
	public Integer getCodigoInicioTurmaTaxi() {
		return codigoInicioTurmaTaxi;
	}
	public void setCodigoInicioTurmaTaxi(Integer codigoInicioTurmaTaxi) {
		this.codigoInicioTurmaTaxi = codigoInicioTurmaTaxi;
	}

}
